﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Terminal.Advanced_AEUC_settings_controls.Settings_AUX;
using System.IO;
using System.Windows.Forms;

namespace Terminal.Advanced_AEUC_settings_controls.Scripting
{
    /// <summary>
    /// Send message script action
    /// </summary>
    class ScrActStrctMemcpy : ScrBasicAction
    {
        /// <summary>
        /// Message sending callback
        /// </summary>
        CommLink.CommLink.MessageSend sendFunc;
        /// <summary>
        /// Semaphore for message reception synchronization
        /// </summary>
        AutoResetEvent waitMsg;
        /// <summary>
        /// Byte array representing the message received.
        /// </summary>
        byte[] msgBytestream;
        /// <summary>
        /// Reference to the messaging script module
        /// </summary>
        ScrMsgCntr msgCntr;

        const int LOAD_CHUNK_SZ = 1024;

        /// <summary>
        /// Public constructor for the script action
        /// </summary>
        /// <param name="iDispName">Action display text</param>
        /// <param name="iUsageHelp">Usage help and comments text for the action</param>
        /// <param name="callback">Callback function to be used for sending messages</param>
        public ScrActStrctMemcpy(string iDispName, string iUsageHelp, CommLink.CommLink.MessageSend callback, ScrMsgCntr iMsgCntr)
            : base(iDispName, iUsageHelp)
        {
            sendFunc = callback;
            waitMsg = new AutoResetEvent(false);
            msgCntr = iMsgCntr;
            msgCntr.DispatchEnabled = false;
            msgCntr.OnMessageReceived += ReceiveMsg;

            usageHelp += "Input params: Marshalling - mem copy GenMSG(message name) + integer component instance id\n + integer adress pointer + integer address offset + payload structure\n";
        }

        /// <summary>
        /// Inform the wait for message action that a message has been received and provide the message data.
        /// </summary>
        /// <param name="byteStream">Byte array of the message data</param>
        public void ReceiveMsg(byte[] byteStream)
        {
            msgBytestream = byteStream;
            waitMsg.Set();
        }

        /// <summary>
        /// Perform the action using specified parameters.
        /// </summary>
        /// <param name="paramList">List of the input/output parameters for the action</param>
        /// <returns>True if action is succesful</returns>
        public override bool Do(List<object> paramList)
        {
            if (breakPoint)
                WaitUserInput();

            if (paramList.Count > 5 && paramList[0].GetType() == typeof(ScrRunTimeEnv)
                                    && paramList[1].GetType() == typeof(GenMSG) 
                                    && paramList[2].GetType() == typeof(int)
                                    && paramList[3].GetType() == typeof(int)
                                    && paramList[4].GetType() == typeof(int)
                                    && paramList[5].GetType() == typeof(GenPayloadStruct))
            {
                GenMSG msgHdr = new GenMSG();
                msgHdr.msgId = new GenMSGId();
                msgHdr.msgStatus = new GenMSGStatus();
                ScrRunTimeEnv rte = (ScrRunTimeEnv)(paramList[0]);
                GenMSG msgMarshalCopy = (GenMSG)paramList[1];
                int CompId = (int)paramList[2];
                int aPtr = (int)paramList[3];
                int aOfst = (int)paramList[4];
                GenPayloadStruct pldStruct = (GenPayloadStruct)paramList[5];

                BinaryReader bRd = new BinaryReader(new MemoryStream(pldStruct.prepareByteStream()));
                ProgressBars.ProgBarsHdl progHdl = rte.scrSetUpProgBar("File transfer", (int)(bRd.BaseStream.Length + (LOAD_CHUNK_SZ - 1)) / LOAD_CHUNK_SZ);
                for (int crntStrmPos = 0; crntStrmPos < bRd.BaseStream.Length; )
                {
                    #region Prepare marhsalling memory copy message

                    GenPayloadStruct.NewPldItem[] runTVar = new GenPayloadStruct.NewPldItem[4];
                    int ittr = 0;

                    runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                    runTVar[ittr].AEUC_DisplayName = "ComponentId";
                    runTVar[ittr].AEUC_Val = CompId;
                    runTVar[ittr].AEUC_Id = (uint)ittr++;

                    runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                    runTVar[ittr].AEUC_DisplayName = "Size";
                    if (bRd.BaseStream.Length - crntStrmPos > LOAD_CHUNK_SZ)
                        runTVar[ittr].AEUC_Val = LOAD_CHUNK_SZ;
                    else
                        runTVar[ittr].AEUC_Val = (int)(bRd.BaseStream.Length - crntStrmPos);

                    runTVar[ittr].AEUC_Id = (uint)ittr++;

                    runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                    runTVar[ittr].AEUC_DisplayName = "Pointer";
                    runTVar[ittr].AEUC_Val = aPtr;
                    runTVar[ittr].AEUC_Id = (uint)ittr++;

                    runTVar[ittr] = new GenPayloadStruct.NewPldItem();
                    runTVar[ittr].AEUC_DisplayName = "Offset";
                    runTVar[ittr].AEUC_Val = aOfst + crntStrmPos;
                    runTVar[ittr].AEUC_Id = (uint)ittr++;
                    #endregion
                    GenPayloadStruct marshallCopy = new GenPayloadStruct("marshall copy", runTVar);
                    #region Prepare the marshalling copy payload
                    byte[] msgByteStream = marshallCopy.prepareByteStream();
                    byte[] nwStream = null;
                    if (bRd.BaseStream.Length - crntStrmPos > LOAD_CHUNK_SZ)
                    {
                        nwStream = bRd.ReadBytes(LOAD_CHUNK_SZ);
                        crntStrmPos += LOAD_CHUNK_SZ;
                    }
                    else
                    {
                        nwStream = bRd.ReadBytes((int)(bRd.BaseStream.Length - crntStrmPos));
                        crntStrmPos += (int)(bRd.BaseStream.Length - crntStrmPos);
                    }
                    byte[] tpStream = new byte[msgByteStream.Length + nwStream.Length];
                    msgByteStream.CopyTo(tpStream, 0);
                    nwStream.CopyTo(tpStream, msgByteStream.Length);
                    msgByteStream = tpStream;
                    #endregion
                    sendFunc(msgMarshalCopy.GenerateMessage(msgByteStream));
                    do
                    {
                        msgCntr.DispatchEnabled = true;
                        waitMsg.WaitOne(-1);
                        if (msgBytestream != null)
                        {
                            msgHdr.msgBytestream = msgBytestream;
                            msgHdr.ParseMessageDataHeaderOnly();

                            if (msgHdr.msgId.UID == msgMarshalCopy.msgId.UID)
                            {
                                break;
                            }
                        }
                    } while (true);

                    rte.scrStepProgBar(progHdl);
                }

                rte.scrRemProgBar(progHdl);
                bRd.Close();
                return true;
            }

            return false;
        }

        /// <summary>
        /// Returns a List containing objects of the parameter list required for the Do function.
        /// </summary>
        /// <returns>List of parameter objects</returns>
        public override List<Type> GetParamList()
        {
            List<Type> paramList = new List<Type>();
            paramList.Add(typeof(ScrRunTimeEnv));
            paramList.Add(typeof(GenMSG));
            paramList.Add(typeof(int));
            paramList.Add(typeof(int));
            paramList.Add(typeof(int));
            paramList.Add(typeof(GenPayloadStruct));

            return paramList;
        }
    }
}



